package cc.rengu.redp.common.redis.impl;

import cc.rengu.redp.common.domain.RedisInfo;
import cc.rengu.redp.common.domain.RedisJedis;
import cc.rengu.redp.common.exception.RedisConnectException;
import cc.rengu.redp.common.function.JedisExecutor;
import cc.rengu.redp.common.redis.RedisInfoService;
import cc.rengu.redp.common.utils.SpringContextUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.ClusterInfo;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Client;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.*;

@Slf4j
@Service
public class RedisInfoServiceImpl implements RedisInfoService {

    @Autowired
    JedisPool jedisPool;
    @Autowired
    RedisTemplate redisTemplate;

    private static Map<String, RedisJedis> clusterNodeJedis = Maps.newHashMap();

    private static String separator = System.getProperty("line.separator");

    /**
     * 处理 jedis请求
     *
     * @param j 处理逻辑，通过 lambda行为参数化
     * @return 处理结果
     */
    private <T> T executeByJedis(JedisExecutor<Jedis, T> j) throws RedisConnectException {
        try (Jedis jedis = jedisPool.getResource()) {
            return j.execute(jedis);
        } catch (Exception e) {
            throw new RedisConnectException(e.getMessage());
        }
    }


    /**
     * 处理 jedis请求
     *
     * @param j 处理逻辑，通过 lambda行为参数化
     * @return 处理结果
     */
    private <T> T executeByClusterNodeJedis(String nodeId, JedisExecutor<Jedis, T> j) throws RedisConnectException {
        RedisJedis node = clusterNodeJedis.get(nodeId);
        Jedis jedis = new Jedis(node.getHost(), node.getPort());
        try {
            log.info("node:{}",node);
            log.info("jedis:{}",jedis);
            jedis.auth(node.getPassWord());
            return j.execute(jedis);
        } catch (Exception e) {
            throw new RedisConnectException(e.getMessage());
        } finally {
            if (null != jedis) {
                jedis.close();
            }
        }
    }

    @Override
    public Map<String, Object> getRedisInfo() throws RedisConnectException {
        Map<String, Object> result = Maps.newHashMap();
        boolean clusterFlag = false;
        List<RedisInfo> infoList = new ArrayList<>();
        try {
            ClusterInfo clusterInfo = this.redisTemplate.getConnectionFactory().getClusterConnection().clusterGetClusterInfo();

            /*** 生成集群信息 ***/
            RedisInfo redisInfo = new RedisInfo();
            redisInfo.setKey("redis_mode");
            redisInfo.setValue("cluster");
            infoList.add(redisInfo);
            for (ClusterInfo.Info info : ClusterInfo.Info.values()) {
                redisInfo = new RedisInfo();
                if (info.name().toLowerCase().startsWith("messages_")) {
                    redisInfo.setKey("cluster_stats_" + info.name().toLowerCase());
                } else {
                    redisInfo.setKey("cluster_" + info.name().toLowerCase());
                }
                redisInfo.setValue(clusterInfo.get(info));
                infoList.add(redisInfo);
            }

            /*** 生成集群节点信息 ***/
            List<RedisClusterNode> nodes = Lists.newArrayList(this.redisTemplate.getConnectionFactory().getClusterConnection().clusterGetNodes());
            Environment environment = SpringContextUtil.getBean(Environment.class);
            String password = environment.getProperty("spring.redis.password");
            for (RedisClusterNode node : nodes) {
                RedisJedis rj = new RedisJedis(node.getId(), node.getHost(), node.getPort(), password);
                clusterNodeJedis.put(node.getId(), rj);
            }
            result.put("clusterNodes", nodes);
            clusterFlag = true;
        } catch (Exception e) {
            if (e instanceof InvalidDataAccessApiUsageException) {
                log.warn("redis请求模式异常：", e.getMessage());
            } else {
                log.error("系统内部异常，异常信息：", e);
            }
        }

        if (!clusterFlag) {
            String info = this.executeByJedis(
                    j -> {
                        Client client = j.getClient();
                        client.info();
                        return client.getBulkReply();
                    }
            );

            String[] strs = Objects.requireNonNull(info).split(separator);
            RedisInfo redisInfo;
            if (strs.length > 0) {
                for (String str1 : strs) {
                    redisInfo = new RedisInfo();
                    String[] str = str1.split(":");
                    if (str.length > 1) {
                        String key = str[0];
                        String value = str[1];
                        redisInfo.setKey(key);
                        redisInfo.setValue(value);
                        infoList.add(redisInfo);
                    }
                }
            }

        }
        result.put("clusterFlag", clusterFlag);
        result.put("redisInfo", infoList);
        return result;
    }

    @Override
    public Map<String, Object> getKeysSize() throws RedisConnectException {

        boolean clusterFlag = false;
        Long dbSize = 0L;
        try {
            dbSize = this.redisTemplate.getConnectionFactory().getClusterConnection().dbSize();
            clusterFlag = true;
        } catch (Exception e) {
            if (e instanceof InvalidDataAccessApiUsageException) {
                //非Redis集群模式
            } else {
                log.error("系统内部异常，异常信息：", e);
            }
        }

        if (!clusterFlag) {
            dbSize = this.executeByJedis(
                    j -> {
                        Client client = j.getClient();
                        client.dbSize();
                        return client.getIntegerReply();
                    }
            );
        }

        Map<String, Object> map = new HashMap<>();
        map.put("create_time", System.currentTimeMillis());
        map.put("dbSize", dbSize);
        return map;
    }

    @Override
    public Map<String, Object> getMemoryInfo() throws RedisConnectException {
        try {
            List<RedisClientInfo> infos = this.redisTemplate.getConnectionFactory().getClusterConnection().getClientList();
        } catch (Exception e) {
            if (e instanceof InvalidDataAccessApiUsageException) {
                //非Redis集群模式
            } else {
                log.error("系统内部异常，异常信息：", e);
            }
        }

        String info = this.executeByJedis(
                j -> {
                    Client client = j.getClient();
                    client.info();
                    return client.getBulkReply();
                }
        );
        String[] strs = Objects.requireNonNull(info).split(separator);
        Map<String, Object> map = null;
        for (String s : strs) {
            String[] detail = s.split(":");
            if ("used_memory".equals(detail[0])) {
                map = new HashMap<>();
                map.put("used_memory", detail[1].substring(0, detail[1].length() - 1));
                map.put("create_time", System.currentTimeMillis());
                break;
            }
        }
        return map;
    }


    /**
     * 获取集群节点 redis 的详细信息
     *
     * @return List
     */
    @Override
    public List<RedisInfo> getClusterNodeInfo(String nodeId, String host, int port) throws RedisConnectException {

        String info = this.executeByClusterNodeJedis(nodeId,
                j -> {
                    Client client = j.getClient();
                    client.info();
                    return client.getBulkReply();
                }
        );

        String[] strs = Objects.requireNonNull(info).split(separator);
        RedisInfo redisInfo;
        List<RedisInfo> infoList = new ArrayList<>();
        if (strs.length > 0) {
            for (String str1 : strs) {
                redisInfo = new RedisInfo();
                String[] str = str1.split(":");
                if (str.length > 1) {
                    String key = str[0];
                    String value = str[1];
                    redisInfo.setKey(key);
                    redisInfo.setValue(value);
                    infoList.add(redisInfo);
                }
            }
        }
        return infoList;
    }

    /**
     * 获取集群节点 redis key 数量
     *
     * @return Map
     */
    @Override
    public Map<String, Object> getKeysSizeClusterNode(String nodeId, String host, int port) throws RedisConnectException {
        Long dbSize = this.executeByClusterNodeJedis(nodeId,
                j -> {
                    Client client = j.getClient();
                    client.dbSize();
                    return client.getIntegerReply();
                }
        );
        Map<String, Object> map = new HashMap<>();
        map.put("create_time", System.currentTimeMillis());
        map.put("dbSize", dbSize);
        return map;
    }

    /**
     * 获取集群节点 redis 内存信息
     *
     * @return Map
     */
    @Override
    public Map<String, Object> getMemoryInfoClusterNode(String nodeId, String host, int port) throws RedisConnectException {
        String info = this.executeByClusterNodeJedis(nodeId, j -> {
            Client client = j.getClient();
            client.info();
            return client.getBulkReply();
        });
        String[] strs = Objects.requireNonNull(info).split(separator);
        Map<String, Object> map = null;
        for (String s : strs) {
            String[] detail = s.split(":");
            if ("used_memory".equals(detail[0])) {
                map = new HashMap<>();
                map.put("used_memory", detail[1].substring(0, detail[1].length() - 1));
                map.put("create_time", System.currentTimeMillis());
                break;
            }
        }
        return map;
    }
}
